область видимости
=================

.. index::
    scope
    область видимости

Как мы уже видели в предыдущей главе, выражения, которые должны выполняться всегда
выполняются в блоке finally, а выражения, которые должны выполняться
при возникновении ошибок, записываются в блоках catch.
Мы можем сделать следующие замечания по поводу использования этих блоков:

* catch и finally нельзя использовать без блока try.
*  Некоторые переменные, которые нужны этим блокам, могут быть недоступны внутри этих блоков:

.. code-block:: d

    void foo(ref int r)
    {
        try
        {
            int addend = 42;
            r += addend;
            mayThrow();
        }
        catch (Exception exc)
        {
            r -= addend; // ошибка компиляции
        }
    }


Эта функция сначала изменяет параметр ссылки, а затем возвращает это изменение
когда возникает исключение. К сожалению, функция addend
доступна только в блоке try, где она и определена.
*(Примечание: Это связано с диапазонами имен, а также временем жизни объектов, о чем будет рассказано в одной из следующих глав.* (:ref:`Время жизни и фундаментальные операции<глава_44_start>`)

Запись всех потенциально несвязанных выражений в единый блок finally в
внизу отделяет эти выражения от фактического кода, с которым они
с которым они связаны.

Операторы области видимости имеют ту же функциональность, что и операторы catch и finally
но во многих отношениях они лучше. Как и finally, три различных
оператора scope связаны с выполнением выражений при выходе из области видимости:

* scope(exit): выражение всегда выполняется при выходе из области видимости,
  независимо от того, успешно или из-за исключения
* scope(success): выражение выполняется только при успешном выходе из области видимости
  успешно
* scope(failure): выражение выполняется только в том случае, если выход из области видимости происходит
  из-за исключения

Хотя эти утверждения тесно связаны с исключениями, их можно использовать без блока try-catch.
В качестве примера напишем приведенную выше функцию с scope(failure) оператором


.. code-block:: d

    void foo(ref int r)
    {
        int addend = 42;
        r += addend;
        scope(failure) r -= addend;
        mayThrow();
    }

Приведенное выше выражение scope(failure) гарантирует, что выражение r -= addend
будет выполнено, если область видимости функции выйдет из нее из-за исключения. Преимуществом
scope(failure) является то, что выражение, возвращающее другое выражение, записывается рядом с ним.
Операторы scope можно задавать и в виде блоков:

.. code-block:: d

    scope(exit)
    {
    // ... expressions ...
    }

Вот еще одна функция, которая проверяет все три этих утверждения:

.. code-block:: d

    void test()
    {
        scope (exit)
            writeln("when exiting 1");
        scope (success)
        {
            writeln("if successful 1");
            writeln("if successful 2");
        }

        scope (failure)
            writeln("if thrown 1");
        scope (exit)
            writeln("when exiting 2");
        scope (failure)
            writeln("if thrown 2");
        throwsHalfTheTime();
    }


Если исключение не выброшено, вывод функции включает только выражения
scope(exit) и scope(success):

.. code-block:: console

    when exiting 2
    if successful 1
    if successful 2
    when exiting 1

Если возникло исключение, вывод включает выражения scope(exit) и
выражения scope(failure):

.. code-block:: console

    if thrown 2
    when exiting 2
    if thrown 1
    when exiting 1
    object.Exception@...: the error message

Как видно из выводов, блоки операторов области видимости выполняются в обратном порядке
порядке. Это связано с тем, что последующий код может зависеть от предыдущих переменных. Выполнение
операторов области видимости в обратном порядке позволяет отменить побочные эффекты предыдущих
выражений в последовательном порядке.
